package com.coocaa.x.service.litewebserver.webserver.servlet;

import android.content.Context;
import android.content.res.AssetManager;

import com.coocaa.x.framework.data.JObject;
import com.coocaa.x.framework.utils.LiteHandlerThread;
import com.coocaa.x.service.litewebserver.webserver.LiteWebHttpd;
import com.coocaa.x.service.litewebserver.webserver.NanoHTTPD;
import com.coocaa.x.service.litewebserver.webserver.NanoHTTPD.Response;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

/**
 * Created by lu on 15-9-7.
 */
public abstract class Servlet implements LiteWebHttpd.ServletHandler {
    public static class SessionHandler {
        private Map<String, LiteWebHttpd.ServletHandler> handlers = new HashMap<String, LiteWebHttpd.ServletHandler>();
        private ServletInfo servletInfo;
        private String servletID;

        public SessionHandler(ServletInfo servletInfo) {
            this.servletInfo = servletInfo;
            servletID = servletInfo.getID();
        }

        public void addHandler(String uri, LiteWebHttpd.ServletHandler handler) {
            synchronized (handlers) {
                handlers.put(uri, handler);
            }
        }

        public LiteWebHttpd.ServletHandler getHandler(NanoHTTPD.IHTTPSession session) {
            synchronized (handlers) {
                String uri = session.getUri();
                String _uri = uri.substring(SERVLET_HEADER.length()).substring(servletID.length() - 1);
                return handlers.get(_uri);
            }
        }

        public Response handle(NanoHTTPD.IHTTPSession session) {
            LiteWebHttpd.ServletHandler h = getHandler(session);
            if (h != null)
                return h.serve(session);
            return null;
        }

        public void clear() {
            synchronized (handlers) {
                handlers.clear();
            }
        }
    }

    public static class ResponseMessage extends JObject {
        public int error_code;
        public String error_msg;
    }

    public static final String SERVLET_HEADER = "/servlet";
    public static final String CACHE_HEADER = "/cache";

    public static class ServletName extends JObject {
        public String vendorName;
        public String servletName;

        public ServletName() {

        }

        public ServletName(String vendorName, String servletName) {
            this.vendorName = vendorName;
            this.servletName = servletName;
        }

        public String getID() {
            String vn = vendorName.trim();
            String sn = servletName.trim();
            if (vn.equals("") && sn.equals(""))
                return "/";
            return new StringBuffer("/").append(vn).append("/").append(sn).append("/").toString();
        }
    }

    public static class ServletInfo extends ServletName {
        public String label;
        public String versionName;
        public long versionCode;
    }

    private static final String ASSETS_WWWROOT = "wwwroot%s";
    protected Context mContext = null;
    private LiteHandlerThread handlerThread = null;
    private boolean bCreated = false;


    private SessionHandler handlers = null;

    public final void setContext(Context c) {
        mContext = c;
    }

    protected final synchronized SessionHandler getSessionHandler() {
        if (handlers == null)
            handlers = new SessionHandler(getServletInfo());
        return handlers;
    }

    protected final void post(Runnable run) {
        if (handlerThread != null)
            handlerThread.post(run);
    }

    public final synchronized void create() {
        if (handlerThread == null) {
            ServletInfo info = getServletInfo();
            String name;
            if (info == null)
                name = info.getID();
            else
                name = toString();
            handlerThread = new LiteHandlerThread(name);
            handlerThread.start();
            handlerThread.post(new Runnable() {
                @Override
                public void run() {
                    onCreate();
                    synchronized (Servlet.this) {
                        bCreated = true;
                        Servlet.this.notifyAll();
                    }
                }
            });
        }
    }

    public final synchronized void destroy() {
        if (handlerThread != null) {
            handlerThread = null;
            handlerThread.post(new Runnable() {
                @Override
                public void run() {
                    if (handlers != null)
                        handlers.clear();
                    handlers = null;
                    onDestroy();
                    synchronized (Servlet.this) {
                        bCreated = false;
                        Servlet.this.notifyAll();
                    }
                }
            });

            synchronized (this) {
                if (bCreated)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        }
    }

    @Override
    public final Response serve(NanoHTTPD.IHTTPSession session) {
        synchronized (this) {
            if (!bCreated)
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            String uri = session.getUri();
            Response r = getIndexURIReponse(uri);
            if (r == null)
                r = getSettingURIReponse(uri);
            if (r == null)
                r = onServe(session);
            if (r == null)
                r = newFixedLengthResponse("not support!!!");
            return r;
        }
    }

    private Response getIndexURIReponse(String uri) {
        if (uri == null || uri.equals("") || uri.equals("/") || uri.contains(INDEX_URI)) {
            ServletInfo info = getServletInfo();
            if (info != null) {
                InputStream is = getResourceFromAssets(info.getID() + INDEX_URI);
                if (is != null) {
                    return newChunkedResponse(Response.Status.OK, NanoHTTPD.MIME_HTML, is);
                }
            }
            return newFixedLengthResponse("not support index.html!!");
        }
        return null;
    }

    private Response getSettingURIReponse(String uri) {
        if (uri == null || uri.equals("") || uri.equals("/") || uri.contains(SETTING_URI)) {
            ServletInfo info = getServletInfo();
            if (info != null) {
                InputStream is = getResourceFromAssets(info.getID() + SETTING_URI);
                if (is != null) {
                    return newChunkedResponse(Response.Status.OK, NanoHTTPD.MIME_HTML, is);
                }
            }
            return newFixedLengthResponse("not support setting.html!!");
        }
        return null;
    }

    protected abstract void onCreate();

    protected abstract void onDestroy();

    protected abstract Response onServe(NanoHTTPD.IHTTPSession session);

    public abstract ServletInfo getServletInfo();

    protected abstract InputStream getResource(String uri);

    protected final InputStream getResourceFromAssets(String uri) {
        AssetManager am = mContext.getAssets();
        try {
            return am.open(String.format(ASSETS_WWWROOT, uri));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static final ResponseMessage SUCCESSFUL_MSG = new ResponseMessage();

    protected static Response newResponseMessage(ResponseMessage msg) {
        if (msg == null)
            return newFixedLengthResponse(SUCCESSFUL_MSG.toJSONString());
        return newFixedLengthResponse(msg.toJSONString());
    }

    /**
     * Create a response with unknown length (using HTTP 1.1 chunking).
     */
    protected static Response newChunkedResponse(Response.IStatus status, String mimeType, InputStream data) {
        return new Response(status, mimeType, data, -1);
    }

    /**
     * Create a response with known length.
     */
    protected static Response newFixedLengthResponse(Response.IStatus status, String mimeType, InputStream data, long totalBytes) {
        return new Response(status, mimeType, data, totalBytes);
    }

    /**
     * Create a text response with known length.
     */
    protected static Response newFixedLengthResponse(Response.IStatus status, String mimeType, String txt) {
        if (txt == null) {
            return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0);
        } else {
            byte[] bytes;
            try {
                bytes = txt.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e);
                bytes = new byte[0];
            }
            return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length);
        }
    }

    /**
     * Create a text response with known length.
     */
    protected static Response newFixedLengthResponse(String msg) {
        return newFixedLengthResponse(Response.Status.OK, NanoHTTPD.MIME_HTML, msg);
    }


    private static final String ID_FORMAT = "/%s/%s/";

    public static String uriToServletID(String uri) {
        String[] aaa = uri.substring(SERVLET_HEADER.length()).split("/");
        if (aaa.length >= 3) {
            return String.format(ID_FORMAT, aaa[1], aaa[2]);
        }
        return null;
    }

    protected static final String INDEX_URI = "index.html";
    protected static final String SETTING_URI = "setting.html";

    public static String getMyURI(String uri) {
        if (uri.endsWith("/"))
            uri = uri.substring(0, uri.length() - 1);
        String _uri = uriToServletID(uri);
        if (_uri == null) {
            if (uri.equals(""))
                return INDEX_URI;
            return uri;
        }
        if (uri.equals(_uri))
            return INDEX_URI;
        return uri.substring(_uri.length());
    }
}
